package lexer

import (
	"errors"
	"fmt"
	"lee/token"
	"unicode"
	"unicode/utf8"
)

const bom = 0xFEFF

type ErrHandle func(err error)

type Lexer struct {
	*File
	ErrCnt    int
	errHandle ErrHandle
}

func (l *Lexer) error(err error) {
	if l.errHandle != nil {
		l.errHandle(err)
	}
	l.ErrCnt++
}

// Init 会初始化一个词法解析器
func (l *Lexer) Init(file *File, err ErrHandle) {
	l.File = file
	l.errHandle = err
	l.next()
	if l.chr == bom {
		l.next() // 忽略 bom
	}
}

// 读取一个 token

func (l *Lexer) Scan() (pos Pos, tok token.Token, lit string) {
	l.skipWhitespace()
	pos = Pos(l.offset - 1)
	chr := l.chr
	// 字符处理
	if isLetter(chr) {
		lit, tok = l.scanIdentifier()
		return
	} else if '0' < chr && chr < '9' {
		fmt.Printf("%c\t", chr)
		lit, tok = l.scanNumber(10)
		return
	} else if chr > 0 && chr < utf8.RuneSelf {
		tok = token.Type([]byte{byte(chr)})
		if tok != token.IDENT {
			return
		}
	}

	switch chr {
	case -1:
		tok = token.EOF
	case '/':
		if l.peek() == '/' || l.peek() == '*' {
			lit, tok = l.scanComment()
		}
	case '"', '\'':
		lit, tok = l.scanString(chr)
	default:
		l.error(fmt.Errorf("illegal token: %c", chr))
		tok = token.ILLEGAL
	}
	return
}

func (l *Lexer) skipWhitespace() {
	for l.chr == ' ' || l.chr == '\t' || l.chr == '\n' /*&& !l.insertSemi*/ || l.chr == '\r' {
		l.next()
	}
}

// scanComment 读取注释
func (l *Lexer) scanComment() (lit string, tok token.Token) {
	// initial '/' already consumed; s.ch == '/' || s.ch == '*'
	// 第一个 '/' 的位置
	offStart := l.offset - 1
	// 单行注释
	if l.peek() == '/' {
		l.next()
		for l.chr != '\n' && l.chr >= 0 {
			l.next()
		}
	} else {
		// 多行注释
		l.next()
		for l.chr >= 0 {
			ch := l.chr
			l.next()
			if ch == '*' && l.chr == '/' {
				l.next()
				break
			}
		}
	}

	// windows 平台下换行符可能是 '\r\n'
	// 注释的换行处 '\r' 需要移除
	//lit = stripCR(lit, lit[1] == '*')
	return string(l.src[offStart:l.offset]), token.COMMENT
}

// 读取当前字符
// 游标移动到下一个字符
func (l *Lexer) next() {
	err := l.File.next()
	if err != nil {
		if err == EOF {
			l.chr = -1
		} else {
			l.error(err)
		}
	}
}

// scanIdentifier 读取标识符
func (l *Lexer) scanIdentifier() (string, token.Token) {
	offStart := l.offset - 1
	for isLetter(l.chr) || isDigit(l.chr) {
		l.next()
	}
	lit := l.src[offStart : l.offset-1]
	return string(lit), token.Type(lit)
}

// scanNumber 读取一个数
func (l *Lexer) scanNumber(base int) (lit string, tok token.Token) {
	// digitVal(l.ch) < 10
	offStart := l.offset - 1
	tok = token.INT

ScanInt:
	for digitVal(l.chr) < base {
		l.next()
	}

	if l.chr == '.' /* && tok == token.INT*/ {
		tok = token.FLOAT
		goto ScanInt
	}
	return string(l.src[offStart:l.offset]), tok
}

// scanNumber 读取一个单行字符串
func (l *Lexer) scanString(quot rune) (lit string, tok token.Token) {
	offStart := l.offset - 1

	for {
		ch := l.chr
		if ch == '\n' || ch < 0 {
			l.error(errors.New("string literal not terminated"))
			break
		}
		l.next()
		if ch == quot {
			break
		}
		if ch == '\\' {
			l.skipEscape(quot)
		}
	}
	lit, tok = string(l.src[offStart:l.offset-1]), token.STRING
	return
}

// scanEscape 跳过转义字符
func (l *Lexer) skipEscape(quote rune) bool {
	var n int
	var base, max uint32
	switch l.chr {
	case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote:
		l.next()
		return true
	case '0', '1', '2', '3', '4', '5', '6', '7':
		n, base, max = 3, 8, 255
	case 'x':
		l.next()
		n, base, max = 2, 16, 255
	case 'u':
		l.next()
		n, base, max = 4, 16, unicode.MaxRune
	case 'U':
		l.next()
		n, base, max = 8, 16, unicode.MaxRune
	default:
		msg := "unknown escape sequence"
		if l.chr < 0 {
			msg = "escape sequence not terminated"
		}
		l.error(errors.New(msg))
		return false
	}

	var x uint32
	for n > 0 {
		d := uint32(digitVal(l.chr))
		if d >= base {
			msg := fmt.Sprintf("illegal character %#U in escape sequence", l.chr)
			if l.chr < 0 {
				msg = "escape sequence not terminated"
			}
			l.error(errors.New(msg))
			return false
		}
		x = x*base + d
		l.next()
		n--
	}

	if x > max || 0xD800 <= x && x < 0xE000 {
		l.error(errors.New("escape sequence is invalid Unicode code point"))
		return false
	}

	return true
}
